import { _decorator, Component, error, Node } from 'cc';
import Singleton from './Singleton';
const { ccclass, property } = _decorator;

//绑定操作，
class Listener{
    //消息名字。
    private _msgName:string = '';
    //绑定这个消息的对象。
    private _target:any = null;
    get target(){
        return this._target;
    }
    //响应后要调用的函数。
    private _cb:Function = null;
    get cb(){
        return this._cb;
    }
    //是否是一次的。
    private _isOnce:boolean = null;
    get isOnce(){
        return this._isOnce;
    }
    constructor(msgName:string,taget:any,cb:Function,isOnce:boolean){
        this._msgName = msgName;
        this._target = taget;
        this._cb = cb;
        this._isOnce = isOnce;
    }

    isEquals(listener:Listener){
        return this._msgName ==listener._msgName
                &&this._cb == listener._cb
                &&this._target == listener._target;
    }
}


@ccclass('Emitter')
export class Emitter{
    private static _instance:Emitter = null;
    public static get instance(){
        if(!Emitter._instance){
            Emitter._instance = new this();
        }
        return Emitter._instance;
    }
    //存储消息名字跟Listener数组。 发送消息后，可以多人响应。
    private _mapListener:Map<string,Listener[]> = new Map();

    //注册接口。
    on(msgName:string,cb:Function,target:any,isOnce:boolean = false){
        //判断
        if(!msgName||msgName.length <=0||!target||!cb){
            return;
        }
        //通过消息到Map中查找Listener 看这个消息是否已经在这里注册过了。
        let arrListener = this._mapListener.get(msgName);
        //如果不存在，第一次注册。
        if(!arrListener){
            //创建一个数组。
            arrListener = [];
            //跟消息绑定存储到map
            this._mapListener.set(msgName,arrListener);
        }
        //new一个Listener对象
        let listener = new Listener(msgName,target,cb,isOnce);
        //判断这个对象的这个函数绑定的listener是否已经存在了，如果是，不需要注册。
        for(let listenerIn of arrListener){
            if(listener.isEquals(listenerIn)){
                error('已经注册过了');
                return;
            }
        }
        arrListener.push(listener);
    }

    //注册的一次性接口
    once(msgName:string,target:any,cb:Function){
        this.on(msgName,target,cb,true);
    }


    //消息名字，携带的信息。
    emit(msgName:string,target?:any,...rest:any[]){
        //判断
        if(!msgName||msgName.length <=0){
            return;
        }
        //裁剪参数数组
        let args = Array.prototype.slice.call(arguments,2);
        //先去获取Listener数组
        let arrListener = this._mapListener.get(msgName);
        if(!arrListener){
            //如果都没有注册，那么都不处理
            return;
        }
        //开始响应
        for(let i = 0;i < arrListener.length;i++){
            //执行数组中每个Listener对象的cb
            let listener = arrListener[i];
            if(listener.target&&listener.target!=target){
                continue;
            }
            //执行cb
            listener.cb.apply(listener.target,args);
            //如果这个listener注册的时候是一次性，那么在响应完后，要把他从数组中排除掉。
            if(listener.isOnce){
                arrListener.splice(i--,1);
            }
        }

        //如果说响应完后，如果arrListener为空，也没必要留着
        if(arrListener.length <=0){
            this._mapListener.delete(msgName);
        }
    }

    emitNoTarget(msgName:string,...rest:any[]){
        //判断
        if(!msgName||msgName.length <=0){
            return;
        }
        //裁剪参数数组
        let args = Array.prototype.slice.call(arguments,1);
        //先去获取Listener数组
        let arrListener = this._mapListener.get(msgName);
        if(!arrListener){
            //如果都没有注册，那么都不处理
            return;
        }
        //开始响应
        for(let i = 0;i < arrListener.length;i++){
            //执行数组中每个Listener对象的cb
            let listener = arrListener[i];
            //执行cb
            listener.cb.apply(listener.target,args);
            //如果这个listener注册的时候是一次性，那么在响应完后，要把他从数组中排除掉。
            if(listener.isOnce){
                arrListener.splice(i--,1);
            }
        }

        //如果说响应完后，如果arrListener为空，也没必要留着
        if(arrListener.length <=0){
            this._mapListener.delete(msgName);
        }
    }

    //清除消息接口。
    //清空所有。 参数0个
    //清除某个消息  参数1个
    //清除某个消息某个对象   参数2个
    //清除某个消息某个对象某个函数。  参数3个
    off(...rest:any[]){
        //获取参数数组
        let args  =Array.from(arguments);
        let length = args.length;
        //如果参数是0个，那么清空
        if(length <=0){
            this._mapListener.clear();
            return;
        }   
        //第一个参数必须是字符串，如果不是，那么不做处理。     
        if(typeof args[0] != 'string'){
            error('清除消息第一个参数必须是字符串');
            return;
        }
        let msgName = args[0];
        //如果是一个的，那么就是清除某个消息
        if(length <=1){
            this._mapListener.delete(msgName);
            return;
        }
        //如果参数是两个：清除某个消息中的Listener数组中的监听者里注册了某个对象的所有listener
        let arrListener = this._mapListener.get(msgName);
        //遍历这个数组，找到跟第二个参数（对象），含有这个对象的listener删掉。
        if(args[1] instanceof Function){
            error('参数2要求不是函数');//如果要也行，函数删除函数相关，对象删除对象相关。
            return;
        }
        if(length <=2){
            
            for(let i = 0;i < arrListener.length ;i++){
                if(args[1] == arrListener[i].target){
                    arrListener.splice(i--,1);                    
                }
            }
            //如果空了，那么删掉
            if(arrListener.length <=0){
                this._mapListener.delete(msgName);
            }
            return;
        }
        if(length <=3){
            for(let i = 0;i < arrListener.length ;i++){
                if(args[1] == arrListener[i].target
                    &&args[2] == arrListener[i].cb){
                    arrListener.splice(i,1);  //因为不应该存在一个消息里面注册了同个对象的同个函数多次。 
                    break;                 
                }
            }

            //如果空了，那么删掉
            if(arrListener.length <=0){
                this._mapListener.delete(msgName);
            }
        }
    }
}


